package edu.lnu.ums.ui;

import edu.lnu.ums.dal.ICharity;
import edu.lnu.ums.dal.IExpenditure;
import edu.lnu.ums.dal.exception.EntityManagerFactoryCreationException;
import edu.lnu.ums.dal.exception.OldVersionSavingException;
import edu.lnu.ums.dal.impl.CharityDAO;
import edu.lnu.ums.dal.impl.ExpenditureDAO;
import edu.lnu.ums.dal.impl.SalaryProcessDAO;
import edu.lnu.ums.entities.Charity;
import edu.lnu.ums.entities.Chart;
import edu.lnu.ums.entities.Expenditures;
import edu.lnu.ums.entities.Human;
import edu.lnu.ums.entities.Institution;
import edu.lnu.ums.entities.MaterialValue;
import edu.lnu.ums.entities.Student;
import edu.lnu.ums.entities.Studying;
import edu.lnu.ums.entities.Teacher;
import edu.lnu.ums.entities.core.AbstractProcess;
import edu.lnu.ums.entities.core.LinkToAnotherDB;

import javax.ejb.EJB;

import org.richfaces.component.html.HtmlTree;
import org.richfaces.event.NodeSelectedEvent;

import edu.lnu.ums.entities.core.UMSObject;
import edu.lnu.ums.entities.dictionaries.Months;
import edu.lnu.ums.entities.dictionaries.UMSType;
import edu.lnu.ums.entities.processes.CharityProcess;
import edu.lnu.ums.entities.processes.ExpendituresProcess;
import edu.lnu.ums.entities.processes.ProfGrowth;
import edu.lnu.ums.entities.processes.SalaryProcess;
import edu.lnu.ums.entities.processes.ScientificWork;
import edu.lnu.ums.service.ITreeService;
import edu.lnu.ums.treestructure.ITreeStructure;
import edu.lnu.ums.treestructure.impl.TreeNode;
import edu.lnu.ums.treestructure.impl.TreeStructureFacade;
import java.util.ArrayList;
import java.util.Calendar;

import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import org.ajax4jsf.context.AjaxContext;
import org.richfaces.component.UITree;
import org.richfaces.component.UITreeNode;
import org.richfaces.event.DropEvent;
import org.richfaces.event.NodeExpandedEvent;
import org.richfaces.model.ListRowKey;
import org.richfaces.model.StackingTreeModelKey;

public class TreeBean {

    private static final String BUNDLE_NAME = "messages";
    private static final String OPTIMISTIC_LOCK_ERROR_MESSAGE = "DATA_ALREADY_EDITED_BY_ANOTHER";
    private static final String CAN_NOT_CREATE_DB_CONNECTION_MESSAGE = "CAN_NOT_CREATE_DB_CONNECTION";
    private static final String DB_CONNECTION_CREATED_SUCC_MESSAGE = "DB_CONNECTION_CREATED_SUCC";
    private static final String PROCESS_MESSAGE = "processMessage";
    private static final String SAVED_SUCCESSFULL_MESSAGE = "SAVED_SUCC";
    private static final String SAVE_FAIL_MESSAGE = "SAVED_FAIL";
    private static final String XHTML_ENDING = ".xhtml";
    private static final String NOTHING_SELECTED_PAGE = "empty";
    private static final String ADD_OBJECT_XHTML = "addObject.xhtml";
    private static final String ADD_EXPENDITURES = "../objects/addExpenditures.xhtml";
    private static final String ADD_DONATION = "../objects/addDonation.xhtml";
    private static final String EMPTY_STRING = "";
    private static final String EMPTY_PAGE = "empty.xhtml";
    private static final String PROCESS_EMPTY_PAGE = "../templates/process.xhtml";
    private Expenditures selectedExpend = new Expenditures();
    private Charity selectedCharity = new Charity();
    private String includedPage = EMPTY_PAGE;
    private String includedDataPage = "../objects/empty.xhtml";
    private UMSObject selectedObject;
    private UMSType selectedType;
    private Months months;
    private Double amount;
    private Double donation;
    private String salary;
    private List<SelectItem> yearsList;
    private String selectedItem;
    private ExpenditureDAO expend= new ExpenditureDAO();
    private CharityDAO charity= new CharityDAO();
    private SalaryProcessDAO salDao= new SalaryProcessDAO();

    public String getSelectedItem() {
        return selectedItem;
    }

    public void setSelectedItem(String selectedItem) {
        this.selectedItem = selectedItem;
    }

    public String getSalary() {
        return salary;
    }

    public void setSalary(String salary) {
        this.salary = salary;
    }

        public Double getDonation() {
        return donation;
    }

    public void setDonation(Double donation) {
        this.donation = donation;
    }

    public List<SelectItem> getYearsList() {
         yearsList=new ArrayList<SelectItem>() {};
         Integer currentYear = Calendar.getInstance().get(Calendar.YEAR);
         for(int i=0;i<5;i++){
         yearsList.add(new SelectItem(String.valueOf(currentYear-i)));
         }
        return yearsList;
    }

    public void setYearsList(List<SelectItem> yearsList) {
        this.yearsList = yearsList;
    }

    public Double getAmount() {
        return amount;
    }

    public void setAmount(Double amount) {
        this.amount = amount;
    }

    public Months getMonths() {
        return months;
    }

    public void setMonths(Months months) {
        this.months = months;
    }

    private String newNodeName;
    private AbstractProcess process;
    private String includedProcessPage = PROCESS_EMPTY_PAGE;
    private List<Chart> charts;

    public String getIncludedDataPage() {
        return includedDataPage;
    }
    @EJB(mappedName = "UMS/ExpenditureDAO/local")
    private IExpenditure expendDao;
    @EJB(mappedName = "UMS/CharityDAO/local")
    private ICharity charityDao;
    @EJB(mappedName = "UMS/TreeService/local")
    private ITreeService treeService;
    @EJB(mappedName = "UMS/TreeStructure/local")
    private ITreeStructure treeStructureBean;
    private TreeStructureFacade treeStructure;

    public TreeBean() {
        charts = new ArrayList<Chart>();
        setSalary("11111");
    }

    @PostConstruct
    public void init() {
        treeStructure = new TreeStructureFacade(treeStructureBean);
    }

    public List<Chart> getCharts() {
        return charts;
    }

    public String processSelection(NodeSelectedEvent event) {
        HtmlTree tree = (HtmlTree) event.getComponent();
        TreeNode selectedNode = (TreeNode) tree.getRowData();

        selectedObject = selectedNode.getData();
        treeStructure.makeClick(selectedNode.getData());

        AjaxContext ac = AjaxContext.getCurrentInstance();
        try {
            ac.addComponentToAjaxRender(tree);
        } catch (Exception e) {
            System.err.print(e.getMessage());
        }

        includedPage = calculateIncludedPage();

        return EMPTY_STRING;

    }

    public void processDrop(DropEvent dropEvent) {
        UITreeNode destNode = (UITreeNode) dropEvent.getSource();
        UITree destTree = destNode.getUITree();

        ListRowKey drag = (ListRowKey) dropEvent.getDragValue();
        ListRowKey drop = (ListRowKey) dropEvent.getDropValue();

        TreeNode dragged = getByListRowKey(drag);
        TreeNode destination = getByListRowKey(drop);

        if (!dragCanBePerformed(dragged, destination)) {
            return;
        }

        treeService.changeParent(dragged.getData(), destination.getData());

        treeStructure.refreshTree();

        AjaxContext ac = AjaxContext.getCurrentInstance();
        try {
            ac.addComponentToAjaxRender(destTree);
        } catch (Exception e) {
            System.err.print(e.getMessage());
        }
    }

    public String saveSelectedObject() {
        try {
            treeService.saveUMSObj(selectedObject);
            showMessage(null, SAVED_SUCCESSFULL_MESSAGE, FacesMessage.SEVERITY_INFO);
        } catch (OldVersionSavingException t) {
            showMessage(null, OPTIMISTIC_LOCK_ERROR_MESSAGE, FacesMessage.SEVERITY_ERROR);
            t.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
            showMessage(null, SAVE_FAIL_MESSAGE, FacesMessage.SEVERITY_ERROR);
            treeStructure.refreshTree();
            selectedObject = null;
            return EMPTY_STRING;
        }

        //treeStructure.refreshTree();
        treeStructure.refreshNode(selectedObject);

        selectedObject = treeService.getById(selectedObject, selectedObject.getId());

        return EMPTY_STRING;
    }

    private void showMessage(String clientId, String key, FacesMessage.Severity severity) {
        String text = null;

        try {
            ResourceBundle bundle =
                    ResourceBundle.getBundle(BUNDLE_NAME, FacesContext.getCurrentInstance().getViewRoot().getLocale());
            text = bundle.getString(key);
        } catch (Exception e) {
            System.out.println(e.toString());
        }

        FacesContext.getCurrentInstance().addMessage(clientId,
                new FacesMessage(severity, text, text));
    }

    public String getIncludedPage() {
        return includedPage;
    }

    public String deleteSelected() {
        if (selectedObject == null) {
            throw new IllegalStateException("Object to delete not selected!");
        }

        if (selectedObject.getParent() != null) {
            treeService.deleteUMSObj(selectedObject);

            //treeStructure.refreshTree();
            treeStructure.removeNode(selectedObject);
            selectedObject = null;
        } else {
            // TODO: add here message
            return EMPTY_STRING;
        }

        includedPage = EMPTY_PAGE;

        return NOTHING_SELECTED_PAGE;
    }

    public String addObject() {
        try {
            UMSObject newObject = (UMSObject) selectedType.getUMSClass().newInstance();
            newObject.setNodeName(newNodeName);
            newObject.setParent(selectedObject);
            newObject.setFactory(selectedObject.getFactory());

            treeService.addUMSObj(newObject);
            treeStructure.addAsChild(selectedObject, newObject);

            selectedObject = newObject;
            includedPage = calculateIncludedPage();



        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
        }

        newNodeName = null;
        selectedType = null;
        return EMPTY_STRING;
    }

    public String addChild() {
        includedPage = ADD_OBJECT_XHTML;

        return EMPTY_STRING;
    }

    public String addExpend() {

        includedDataPage = ADD_EXPENDITURES;

        return EMPTY_STRING;
    }

     public String addDonation() {

        includedDataPage = ADD_DONATION;

        return EMPTY_STRING;
    }

    public String cancelAdding() {
        includedPage = calculateIncludedPage();

        return EMPTY_STRING;
    }

    public TreeNode getRoot() {
        return treeStructure.getRoot();
    }

    public UMSObject getSelectedObject() {
        return selectedObject;
    }

    public UMSType getSelectedType() {
        return selectedType;
    }

    public void setSelectedType(UMSType selectedType) {
        this.selectedType = selectedType;
    }

    public String getNewNodeName() {
        return newNodeName;
    }

    public void setNewNodeName(String newNodeName) {
        this.newNodeName = newNodeName;
    }

    public boolean getShowProcessPanel() {
        return getProcess() != null;
    }

    public void resetProcessPanel() {
        process = null;
    }

    public void amortizationClick() {
        Class<?> amortizationClass = ((MaterialValue) selectedObject).getAmortizationClass();
        process = treeService.getProcessByClassAndObject(amortizationClass, selectedObject);
        includedProcessPage = calculateIncludedProcessPage();
    }

    public void scientificWorkClick() {
        Class<?> scientificClass = ((Studying) selectedObject).getScientificWorkClass();
        process = treeService.getProcessByClassAndObject(scientificClass, selectedObject);
        includedProcessPage = calculateIncludedProcessPage();
    }
     public void scienceWorkClick() {
        Class<?> scientificClass = ((Teacher) selectedObject).getScientificWorkClass();
        process = treeService.getProcessByClassAndObject(scientificClass, selectedObject);
        includedProcessPage = calculateIncludedProcessPage();
    }

     public void profGrowthClick() {
        Class<?> profClass = ((Human) selectedObject).getProfGrowthClass();
        process = treeService.getProcessByClassAndObject(profClass, selectedObject);
        includedProcessPage = calculateIncludedProcessPage();
    }


    public void expendituresProcClick() {
        Class<?> expendituresClass = ((Institution) selectedObject).getExpendituresProcClass();
        process = treeService.getProcessByClassAndObject(expendituresClass, selectedObject);
        includedProcessPage = calculateIncludedProcessPage();

    }

   public void expendClick() {


        saveProcess();
        Class<?> expendituresClass = ExpendituresProcess.class;
        process = treeService.getProcessByClassAndObject(expendituresClass, selectedObject);
  
        ExpendituresProcess expProc = (ExpendituresProcess) process;
        Expenditures newObject = new Expenditures();
         selectedExpend = newObject;
         
         expProc.getExpenditure().add(selectedExpend);

         selectedExpend.setParent(expProc);
         selectedExpend.setYears(selectedItem);
         selectedExpend.setAmount(amount);
         selectedExpend.setMonths(months);

         expendDao.persist(selectedObject.getFactory(), selectedExpend);

         showMessage(PROCESS_MESSAGE, SAVED_SUCCESSFULL_MESSAGE, FacesMessage.SEVERITY_INFO);

         newObject.setFactory(selectedExpend.getFactory());

    }

     public void saveDonation() {


        saveProcess();
        Class<?> charityClass = CharityProcess.class;
        process = treeService.getProcessByClassAndObject(charityClass, selectedObject);

        CharityProcess charProc = (CharityProcess) process;
        Charity newObject = new Charity();
         selectedCharity = newObject;

         charProc.getCharity().add(selectedCharity);

         selectedCharity.setDonation(donation);
         selectedCharity.setYears(selectedItem);
         selectedCharity.setParent(charProc);


         charityDao.persist(selectedObject.getFactory(), selectedCharity);

         showMessage(PROCESS_MESSAGE, SAVED_SUCCESSFULL_MESSAGE, FacesMessage.SEVERITY_INFO);

         newObject.setFactory(selectedCharity.getFactory());

    }

     public void calculateSalary(){
         Double sum;
         sum= Double.valueOf(getSalary());
         sum=sum-sum*3.1/100;
         if(sum>434.5){
             sum=sum-(sum-434.5)*15/100;
         }
         setSalary(sum.toString());
         ((SalaryProcess) process).setRealSalary(sum);

         saveProcess();

         includedDataPage="../objects/showSalary.xhtml";
      
     }
  
       public void showScientificGraphic() {

        charts.clear();
        charts.add(new Chart(((ScientificWork) process).getInnovation(), "innovation"));
        charts.add(new Chart(((ScientificWork) process).getComplexity(), "complexity"));
        charts.add(new Chart(((ScientificWork) process).getPerformance(), "performance"));
        charts.add(new Chart(((ScientificWork) process).getProtection(), "protection"));

    }

     public void showExpendituresGraphic() {

         charts.clear();
        for(Expenditures exp : expend.getAllByYear(selectedObject.getFactory(),selectedItem,process.getId())){
            charts.add(new Chart(exp.getAmount().intValue(),exp.getMonths().getName()));
        }

    }
     public void showGrowth(){

        charts.clear();
        charts.add(new Chart(((ProfGrowth) process).getMathPart(), "Mathematical"));
        charts.add(new Chart(((ProfGrowth) process).getMemoryPart(), "Memore"));
        charts.add(new Chart(((ProfGrowth) process).getSpacePart(), "Spatial"));
        charts.add(new Chart(((ProfGrowth) process).getVerbalPart(), "Verbal"));

     }
     public void showRelation(){

         charts.clear();
         double sum=0;
         int don=0;
         for(Expenditures exp : expend.getAllByYear(selectedObject.getFactory(),selectedItem,process.getId())){
             sum+=exp.getAmount();
         }
         try{
         don=charity.getDonationByYear(selectedObject.getFactory(), selectedItem, process.getId()).getDonation().intValue();
         }
         catch(javax.persistence.NoResultException e){

         }
        charts.add(new Chart( don, "Charity"));
        charts.add(new Chart((int)sum, "Expenditures"));
     }


    public void processExpand(NodeExpandedEvent event) {
        HtmlTree tree = (HtmlTree) event.getComponent();
        TreeNode selectedNode = (TreeNode) tree.getRowData();
        treeStructure.makeClick(selectedNode.getData());
    }

    public AbstractProcess getProcess() {
        return process;
    }

    public String getIncludedProcessPage() {
        return includedProcessPage;
    }

    public void saveProcess() {
        try {
            process = treeService.saveProcess(process);
            process.setFactory(selectedObject.getFactory());
            showMessage(PROCESS_MESSAGE, SAVED_SUCCESSFULL_MESSAGE, FacesMessage.SEVERITY_INFO);
        } catch (OldVersionSavingException t) {
            showMessage(PROCESS_MESSAGE, OPTIMISTIC_LOCK_ERROR_MESSAGE, FacesMessage.SEVERITY_ERROR);
            process = treeService.getProcessByClassAndObject(process.getClass(), selectedObject);
        }
    }

    public Expenditures getSelectedExpend() {
        return selectedExpend;
    }

    public void setSelectedExpend(Expenditures selectedExpend) {
        this.selectedExpend = selectedExpend;
    }

    public void connect() {
        try {
            saveSelectedObject();
            ((LinkToAnotherDB) selectedObject).connect();
            treeStructure.refreshTree();
            showMessage(null, DB_CONNECTION_CREATED_SUCC_MESSAGE, FacesMessage.SEVERITY_INFO);
        } catch (EntityManagerFactoryCreationException ex) {
            ex.printStackTrace();
            showMessage(null, CAN_NOT_CREATE_DB_CONNECTION_MESSAGE, FacesMessage.SEVERITY_ERROR);
        }
    }

    public void disconnect() {
        ((LinkToAnotherDB) selectedObject).disconnect();
        treeStructure.refreshTree();
    }

    private String calculateIncludedPage() {
        return selectedObject.getClass().getSimpleName() + XHTML_ENDING;
    }

    private String calculateIncludedProcessPage() {
        if (process == null) {
            return PROCESS_EMPTY_PAGE;
        } else {
            return "../processes/" + process.getClass().getSimpleName() + XHTML_ENDING;
        }
    }

    @SuppressWarnings("rawtypes")
    private TreeNode getByListRowKey(ListRowKey rowKey) {
        Iterator it = rowKey.iterator();
        // skipping root object
        it.next();
        TreeNode retVal = treeStructure.getRoot();

        while (it.hasNext()) {
            StackingTreeModelKey key = (StackingTreeModelKey) it.next();
            retVal = retVal.getChildren().get((Integer) key.getModelKey());
        }

        return retVal;
    }

    private boolean dragCanBePerformed(TreeNode dragged, TreeNode destination) {
        return !(treeStructure.getRoot().equals(dragged)
                || dragged.getParent().equals(destination)
                || dragged.equals(destination))
                && (dragged.getData().getFactory() == destination.getData().getFactory());
    }
}
